package cn.skyclass.controls
{
	import cn.skyclass.beans.IBaseBean;
	import cn.skyclass.events.ListItemEvent;
	
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.utils.Dictionary;
	
	import mx.collections.ArrayCollection;
	import mx.containers.Canvas;
	import mx.core.Container;
	import mx.core.ScrollPolicy;
	import mx.effects.Move;
	import mx.events.ResizeEvent;
	
	
	/**
	 * 实现在一个区域中根据数据源显示多个自定义控件，
	 * 其控件布局方式类似 Box，一个接一个的自动排列。
	 * 自定义的控件需要实现 cn.skyclass.controls.IListItem 接口
	 */
	[Event(name="addItem", type="cn.skyclass.events.ListItemEvent")]
	[Event(name="removeItem", type="cn.skyclass.events.ListItemEvent")]
	[Event(name="itemCreated", type="cn.skyclass.events.ListItemEvent")]
	[Event(name="selectItem", type="cn.skyclass.events.ListItemEvent")]
	[Event(name="doubleClickItem", type="cn.skyclass.events.ListItemEvent")]
	[Bindable]
	public class ListItemBox extends Canvas
	{
		public function ListItemBox(){
			this.horizontalScrollPolicy = ScrollPolicy.OFF;
			this.addEventListener(ResizeEvent.RESIZE, this.layoutItems);
		}
		
		/**
		 * 用于存放 item 的容器
		 */
		protected function get itemContainer():Container{
			return this as Container;
		}	
			
		
		/**
		 * item 间的水平和垂直间隙
		 */
		public var itemHorizontalGap:int = 5;
		public var itemVerticalGap:int = 5;
		
		/**
		 * 放置 item 时，此 box 的左边和顶部需要留出的空白部分
		 */
		public var paddingLeft:int = 0;
		public var paddingTop:int = 0;
		
		/**
		 * item 的 styleName, width, height
		 */
		public var itemStyleName:String = null;
		public var itemWidth:int = 0;						
		public var itemHeight:int = 0;	
		
		/**
		 * 需要显示的列数，如果 <= 0，则根据控件的宽度来动态确定列数
		 */
		public var columnNum:int = 0;	
		
		/**
		 * item 创建后，将其移动到适合位置的所需要的时间
		 */						
		public var itemMoveDuration:int = 1;	// 1 ms
		
		/**
		 * selectedItem 中的数据
		 */
		public var selectedItemData:Object;
		
		/**
		 * 处于选中状态的 IListItem ，若有多个被选中则返回第一个
		 */
		private var _selectedItem:IListItem;
		public function get selectedItem():IListItem{
			return this._selectedItem;
		}
		public function set selectedItem(value:IListItem):void{
			this._selectedItem = value;
			this.selectedItemData = (value == null) ? null : value.datasource;
		}
		
		/**
		 * 处于选中状态的多个 IListItem 列表
		 */
		public var selectedItems:ArrayCollection;
		
		
		/**
		 * 用于显示 item 数据的控件类型，需要实现 IListItem 接口
		 */
		public var listItemDisplayCla:Class = null;
		
		/**
		 * 所有 itemDisplayCla 都有的公共属性值
		 */
		public var itemPropsValue:Dictionary = null;
		
		/**
		 * 用于显示的数据源
		 */
		private var _dataProvider:ArrayCollection = null;
		public function get dataProvider():ArrayCollection{
			return this._dataProvider;
		}
		public function set dataProvider(value:ArrayCollection):void{
			this._dataProvider = value;
			this.createAndFillItems(value, this.listItemDisplayCla, this.itemPropsValue);
		}
		
		/**
		 * 当前显示的列数
		 */
		public var currentColumnNum:int;
		
		
		/**
		 * 创建 item 显示实例，并填充其数据源，然后加入孩子显示列表。
		 * 
		 * dataProvider : 数据源列表，其列表长度将决定显示实例的个数，即为每一个数据项创建一个显示实例
		 * listItemDisplayCla : 显示实例的类型，此类型需要实现 IListItem 接口，可参考 ListItemCanvas
		 * itemPropsValue : 用于设置每个 itemDisplayCla 的相关属性
		 * autoLayoutItems : 是否自动调整所有子控件的位置，将其移动到合适的位置
		 * removeCurrentItems : 是否移除现有的全部子控件
		 */
		public function createAndFillItems(dataProvider:ArrayCollection, listItemDisplayCla:Class, 
			itemPropsValue:Dictionary=null, autoLayoutItems:Boolean=true, removeCurrentItems:Boolean=true):void{
			
			this._dataProvider = dataProvider;
			this.listItemDisplayCla = listItemDisplayCla;
			this.itemPropsValue = itemPropsValue;
			if(this.listItemDisplayCla == null || this.dataProvider == null){ return; }	
			
			if(removeCurrentItems){
				this.clearItems();
			}
			
			for each(var obj:Object in this.dataProvider){
				this.createAndFillItem(obj);
			}
			
			if(autoLayoutItems){
				this.moveItemsFromLeftTop();
			}
		}

		/**
		 * 在指定的位置添加一个子控件
		 * 
		 * @param li 需要添加的子控件的实例
		 * @param index 新加的控件在子控件列表中的位置。默认为 -1 ，即追加在最后的位置
		 */				
		public function addListItemAt(li:IListItem, index:int=-1):void{
			this.addChildItemAt(li);
			this.onItemCreated(li);
			li.init();
		}
		
		protected function createAndFillItem(datasource:Object):void{
			var li:IListItem = new listItemDisplayCla();
			
			// add to child display list immediately
			this.addChildItemAt(li);
			
			// set datasource properties
			li.datasource = datasource;
			
			this.onItemCreated(li);
			
			// 待此 item 控件加入到显示列表中 并 设置了属性和监听器后，再调用自定义初始化函数
			li.init();			
		}
		
		protected function addChildItemAt(li:IListItem, index:int=-1):void{
			if(li is DisplayObject){
				if(index >= 0){
					this.itemContainer.addChildAt(li as DisplayObject, index);
				}else{
					this.itemContainer.addChild(li as DisplayObject);
				}
			}
		}
		
		protected function onItemCreated(li:IListItem):void{
			// set properties
			this.setItemProperties(li, itemPropsValue);			
			
			// register listeners
			DisplayObject(li).addEventListener(MouseEvent.CLICK, this.selectHandler);
			DisplayObject(li).addEventListener(MouseEvent.DOUBLE_CLICK, this.doubleClickHandler);					
			
			// notify item created
			var index:int = this.itemContainer.getChildIndex(li as DisplayObject);
			dispatchEvent(new ListItemEvent(ListItemEvent.ITEM_CREATED, li, index));			
		}
		
        protected function setItemProperties(li:IListItem, propsValue:Dictionary):void{
        	var item:DisplayObject = li as DisplayObject;
        	
        	if(this.itemStyleName){
        		try{
        			item["styleName"] = this.itemStyleName;
        		}catch(e:Error){ trace(e); }	        		
        	}
        	if(this.itemHeight > 0){
        		item.height = this.itemHeight;
        	}
        	if(this.itemWidth > 0){
        		item.width = this.itemWidth;
        	}	    
        	    	
			if(propsValue != null){
				for(var pn:String in propsValue){
					try{
						item[pn] = propsValue[pn];
					}catch(e:Error){ trace(e); }
				}
			}					
        }
        
        
        /**
        * 将某个属性值赋给所有列表项
        */
        public function setItemsPropValue(propName:String, propValue:Object):void{
        	for each(var o:DisplayObject in this.itemContainer.getChildren()){
        		try{
        			o[propName] = propValue;
        		}catch(e:Error){ trace(e); }
        	}
        }   
        
        
        /**
        * 清空此 Box 中的全部 item
        */
        public function clearItems():void{
        	// clear all children and selection info
			this.itemContainer.removeAllChildren();	
			this.selectedItem = null;
			this.selectedItemData = null;
			this.selectedItems = null;
        }
		
		
		/**
		* 从左上角移动至全部的容器空间
		*/
		public function moveItemsFromLeftTop():void{
			this.layoutItems();
		}
		
		protected function layoutItems(e:Event=null):void{
			if(this.itemContainer.getChildren().length == 0){ return; }
			
			var item:DisplayObject = this.itemContainer.getChildAt(0);
			var maxCol:int = (this.itemContainer.width - this.paddingLeft)
					/ (item.width + this.itemHorizontalGap);
			if((this.columnNum > 0) && (this.columnNum < maxCol)){
				maxCol = this.columnNum;
			}
			if(maxCol == 0){ maxCol = 1; }
			this.currentColumnNum = maxCol;
			
			var widthPerCol:int = (this.itemContainer.width - this.paddingLeft) / maxCol;
			var heigthPerRow:int = item.height + this.itemVerticalGap;
			  
            var row:int = 0;
            var col:int = -1;	
			var move:Move = null;
			for each(var o:DisplayObject in this.itemContainer.getChildren()){
				col++;
				if(col > maxCol - 1){
					col = 0; row++; // new row
				} 
				
				move = new Move(o);
				move.xTo = this.paddingLeft + col * widthPerCol;
				move.yTo = this.paddingTop + row * heigthPerRow;
				move.duration = this.itemMoveDuration;
				move.play();					
			}
			
			this.itemContainer.verticalScrollPosition = 0;
		}
		
		
		public function setSelectedItem(itemDataOrDisplayComponent:Object):IListItem{
			if(itemDataOrDisplayComponent is DisplayObject){
				var i:int = this.itemContainer.getChildIndex(itemDataOrDisplayComponent as DisplayObject);
				if(i >= 0){
					this.selectedItem = itemDataOrDisplayComponent as IListItem;
					return this.selectedItem;
				}
			}else if(itemDataOrDisplayComponent is IBaseBean){
				var beanData:IBaseBean = itemDataOrDisplayComponent as IBaseBean;
				for each(var item:IListItem in this.itemContainer.getChildren()){
					if(beanData.equals(item.datasource)){
						this.selectedItem = item;
						return this.selectedItem;
					}
				}				
			}else if(itemDataOrDisplayComponent != null){
				for each(var item1:IListItem in this.itemContainer.getChildren()){
					if(item1.datasource == itemDataOrDisplayComponent){
						this.selectedItem = item1;
						return this.selectedItem;
					}
				}
			}
			return null;
		}
		
		public function scrollToSelectdItem():void{
			/*
			var item:DisplayObject = this.selectedItem as DisplayObject;
			if(!item){ return; }
			
			var index:int = this.itemContainer.getChildIndex(item);
			var itemY:int = item.height * (index / this.currentColumnNum);
			var cHeight:int = this.itemContainer.height;
			var cPos:int = this.itemContainer.verticalScrollPosition;
			
			if(itemY + item.height > cPos + cHeight){
				this.itemContainer.verticalScrollPosition += item.height;
			}
			*/
		}
		
		
		protected function selectHandler(e:MouseEvent):void{
			dispatchItemEvent(ListItemEvent.SELECT_ITEM, e.currentTarget);
		}
		
		protected function doubleClickHandler(e:MouseEvent):void{
			dispatchItemEvent(ListItemEvent.DOUBLE_CLICK_ITEM, e.currentTarget);
		}
		
		protected function dispatchItemEvent(type:String, item:Object):void{
			this.selectedItem = item as IListItem;
			
			var lie:ListItemEvent = new ListItemEvent(type, this.selectedItem);
			lie.itemIndex = this.itemContainer.getChildIndex(item as DisplayObject);
			lie.itemDatasource = this.selectedItem.datasource;
			dispatchEvent(lie);			
		}
	}
}